﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Linq.Expressions;

namespace AxeFrog.Data
{
	public abstract class Repository<TEntity, TID> : IRepository<TEntity, TID>
		where TEntity : IIdentifiable<TID>
	{
		[Import]
		private ConnectionContextProvider _ccp;

		protected ConnectionContext Connect()
		{
			return _ccp.Connect();
		}

		protected ConnectionContext Connect(string connectionStringName)
		{
			return _ccp.Connect().Specify(connectionStringName);
		}

		public virtual TEntity Get(Expression<Func<TEntity, bool>> expr)
		{
			using(var cc = Connect())
				return cc.Select<TEntity, TID>(expr);
		}

		public virtual TEntity GetById(TID id)
		{
			using(var cc = Connect())
				return cc.Select<TEntity, TID>(id);
		}

		public virtual List<TEntity> ListById(IEnumerable<TID> ids)
		{
			using(var cc = Connect())
				return cc.SelectMany<TEntity, TID>(ids);
		}

		public virtual List<TEntity> List()
		{
			using(var cc = Connect())
				return cc.SelectAll<TEntity, TID>();
		}

		public virtual List<TEntity> List(Expression<Func<TEntity, bool>> expr)
		{
			using(var cc = Connect())
				return cc.SelectMany<TEntity, TID>(expr);
		}

		public virtual void Store(TEntity entity)
		{
			using(var cc = Connect())
				cc.Store<TEntity, TID>(entity);
		}

		public virtual void Store(IEnumerable<TEntity> entities)
		{
			using(var cc = Connect())
				cc.StoreMany<TEntity, TID>(entities);
		}

		public virtual void Delete(TID id)
		{
			using(var cc = Connect())
				cc.Delete<TEntity, TID>(id);
		}

		public virtual void Delete(Expression<Func<TEntity, bool>> expr)
		{
			using(var cc = Connect())
				cc.Delete<TEntity, TID>(expr);
		}

		public virtual int Count()
		{
			using(var cc = Connect())
				return cc.Count<TEntity, TID>();
		}

		public virtual int Count(Expression<Func<TEntity, bool>> expr)
		{
			using(var cc = Connect())
				return cc.Count<TEntity, TID>(expr);
		}

		#region IRepository Members

		object IRepository.GetById(object id)
		{
			if(!(id is TID))
				throw new ArgumentException("id must be of type " + typeof(TID).FullName, "id");
			return GetById((TID)id);
		}

		List<object> IRepository.ListById(System.Collections.IEnumerable ids)
		{
			return ListById(ids.Cast<TID>()).Cast<object>().ToList();
		}

		void IRepository.Store(object entity)
		{
			if(!(entity is TEntity))
				throw new ArgumentException("entity must be of type " + typeof(TEntity).FullName, "entity");
			Store((TEntity)entity);
		}

		void IRepository.Delete(object id)
		{
			if(!(id is TID))
				throw new ArgumentException("id must be of type " + typeof(TID).FullName, "id");
			Delete((TID)id);
		}

		#endregion
	}
}
